package org.particlethink.template;

import org.apache.poi.xwpf.usermodel.*;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class WordTemplate {
	private static final ThreadLocal<Map<String, List<String>>> tlMap = new ThreadLocal<>(); // 保存

	public static <T> void genWordWithTemplate(String templatePath, String outPath, Class<T> clazz, T data) throws Exception {
		tlMap.set(new HashMap<>());
		try (InputStream fileInputStream = Files.newInputStream(Paths.get(templatePath))) {
			XWPFDocument document = new XWPFDocument(fileInputStream);
			List<String> parseString = getParseString(document);

			Map<String, Object> map = new HashMap<>();

			for (String templateWord : parseString) {
				Matcher matcher = onlyStringRegex.matcher(templateWord);
				if (matcher.find()) {
					String group = matcher.group();
					map.put(templateWord, getParam(data, clazz, group));
				}
			}

			Map<String, List<String>> stringListMap = tlMap.get();

			for (XWPFParagraph paragraph : document.getParagraphs()) {
				paragraph.getRuns().forEach(run -> {
					String text = run.text();
					List<String> parses = stringListMap.get(text);
					if (parses == null || parses.isEmpty()) {
						return;
					}
					for (String template : parses) {
						Object templateValue = map.get(template);
						text = text.replaceAll(Pattern.quote(template), templateValue.toString());
					}
					run.setText(text, 0);
				});
			}

			for (XWPFTable table : document.getTables()) {
				List<XWPFTableRow> rows = table.getRows();
				for (int n = 0; n < rows.size(); n++) {
					XWPFTableRow row = rows.get(n);
					List<XWPFTableCell> cells = row.getTableCells();
					for (int j = 0; j < cells.size(); j++) {
						XWPFTableCell cell = cells.get(j);
						String text = cell.getText();
						List<String> parses = stringListMap.get(text);
						if (parses == null || parses.isEmpty()) {
							continue;
						}
						String value = "";
						List<String> listTemplate = new ArrayList<>();
						for (String template : parses) {
							Object templateValue = map.get(template);
							if (templateValue == null) {
								continue;
							}
							if (templateValue instanceof List) {
								listTemplate.add(template);
								continue; // 循环的后处理
							} else {
								value = templateValue.toString();
							}
							text = text.replaceAll(template, value);
						}
						cell.setText(text);

						for (String template : listTemplate) {
							List templateValue = (List) map.get(template);
							for (int k = 0; k < templateValue.size(); k++) {
								Object obj = templateValue.get(k);
								String newText = text.replaceAll(Pattern.quote(template), obj.toString());
								XWPFTableRow newRow = table.getRow(k + n);
								if (newRow == null) {
									newRow = table.createRow();
								}
								XWPFTableCell newCell = newRow.getCell(j);
								if (newCell == null) {
									newCell = newRow.addNewTableCell();
								}
								newCell.setText(newText);
							}
						}
					}
				}
			}

			document.write(Files.newOutputStream(Paths.get(outPath)));
		} finally {
			tlMap.remove();
		}
	}

	private final static String parseRegexString = "\\$\\{[a-zA-Z0-9_.]+}";
	private final static Pattern parseRegex = Pattern.compile(parseRegexString);
	private final static String onlyString = "[a-zA-Z0-9_.]+";
	private final static Pattern onlyStringRegex = Pattern.compile(onlyString);

	public static List<String> getParseString(XWPFDocument doc) {
		List<XWPFParagraph> paragraphs = doc.getParagraphs();
		List<String> collect = new ArrayList<>();
		paragraphs.stream().map(XWPFParagraph::getRuns).forEach(xwpfRuns -> {
			xwpfRuns.stream().map(XWPFRun::text).map(WordTemplate::parse).forEach(collect::addAll);
//			collect.addAll(.collect(Collectors.toList()));
		});
		List<XWPFTable> tables = doc.getTables();
		for (XWPFTable table : tables) {
			table.getRows().forEach(row -> {
				List<XWPFTableCell> tableCells = row.getTableCells();
				tableCells.forEach(cell -> {
					String text = cell.getText();
					List<String> parse = WordTemplate.parse(text);
					if (!parse.isEmpty()) {
						collect.addAll(parse.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList()));
					}
				});
			});
		}
		return collect.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
	}

	public static List<String> parse(String content) {
		Matcher matcher = parseRegex.matcher(content);
		List<String> collect = new ArrayList<>();
		while (matcher.find()) {
			String group = matcher.group();
			collect.add(group);
		}
		Map<String, List<String>> stringListMap = tlMap.get();
		if (stringListMap != null) {
			stringListMap.put(content, collect);
		}

		return collect;
	}

	public static Object getParam(Object data, Class<?> clazz, String name) throws NoSuchFieldException, IllegalAccessException {
		if (name.contains(".")) {
			String[] names = name.split("\\.", 2);
			if (names.length != 2) {
				return "";
			}

			Object o = clazz.getDeclaredField(names[0]).get(data);

			if (o instanceof List) {
				List<Object> result = new ArrayList<>();
				List list = (List) o;
				for (Object to : list) {
					result.add(getParam(to, to.getClass(), names[1]));
				}
				return result;
			}

			return getParam(o, o.getClass(), names[1]);
		}
		Field declaredField = clazz.getDeclaredField(name);
		return declaredField.get(data);
	}
}
